package org.springframework.security.providers.dao;

import com.je.cache.service.rbac.UserErrorPwCacheManager;
import com.je.core.constants.LoginErrorType;
import com.je.core.security.SecurityManagerImpl;
import com.je.core.security.expand.UsernamePasswordAuthenticationExpand;
import com.je.core.util.StringUtil;
import com.je.core.util.WebUtils;
import com.je.rbac.model.EndUser;
import org.springframework.dao.DataAccessException;
import org.springframework.security.AuthenticationException;
import org.springframework.security.AuthenticationServiceException;
import org.springframework.security.BadCredentialsException;
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
import org.springframework.security.providers.encoding.PasswordEncoder;
import org.springframework.security.providers.encoding.PlaintextPasswordEncoder;
import org.springframework.security.userdetails.UserDetails;
import org.springframework.security.userdetails.UserDetailsService;
import org.springframework.security.userdetails.UsernameNotFoundException;
import org.springframework.util.Assert;

public class DaoAuthenticationProvider
  extends AbstractUserDetailsAuthenticationProvider
{
  private PasswordEncoder passwordEncoder = new PlaintextPasswordEncoder();
  private SaltSource saltSource;
  private UserDetailsService userDetailsService;
  private boolean includeDetailsObject = true;
  
  @Override
  protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication)
    throws AuthenticationException
  {
    Object salt = null;
    if (this.saltSource != null) {
      salt = this.saltSource.getSalt(userDetails);
    }
    if ("1".equals(((EndUser)userDetails).getSingleLogin())) {
      return;
    }
    if (authentication.getCredentials() == null) {
      throw new BadCredentialsException(this.messages.getMessage(
        "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"), 
        this.includeDetailsObject ? userDetails : null);
    }
    String presentedPassword = authentication.getCredentials().toString();
    if (!this.passwordEncoder.isPasswordValid(userDetails.getPassword(), presentedPassword, salt)) {
    	String errorVal=WebUtils.getSysVar("JE_CORE_ERRORPW")+"";
    	int errorCs=0;
    	if(StringUtil.isNotEmpty(errorVal)){
    		errorCs=Integer.parseInt(errorVal+"");
    	}
    	if(errorCs>2){
        	String userId=((EndUser)userDetails).getUserId();
        	Integer errorPw= UserErrorPwCacheManager.getCacheValue(userId);
        	if(errorPw!=null){
                UserErrorPwCacheManager.putCache(userId, errorPw+1);
        	}else{
                UserErrorPwCacheManager.putCache(userId, 1);
        	}
        	int cha=errorCs-errorPw;
        	if(cha>0 && cha<=2){
        		throw new UsernameNotFoundException(LoginErrorType.WARNLOCKED+"_"+errorPw);
        	}else if(cha<=0){
        		throw new UsernameNotFoundException(LoginErrorType.USERLOCKED);
        	}
    	}
      throw new BadCredentialsException(this.messages.getMessage(
        "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"), 
        this.includeDetailsObject ? userDetails : null);
    }
  }
  
  @Override
  protected void doAfterPropertiesSet()
    throws Exception
  {
    Assert.notNull(this.userDetailsService, "A UserDetailsService must be set");
  }
  
  @Override
  protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)throws AuthenticationException
  {
	  UserDetails loadedUser;
    try
    {
      SecurityManagerImpl service = (SecurityManagerImpl)getUserDetailsService();
      service.setParams(((UsernamePasswordAuthenticationExpand)authentication).getParams());
      service.setRequest(((UsernamePasswordAuthenticationExpand)authentication).getRequest());
      loadedUser = getUserDetailsService().loadUserByUsername(username);
    }
    catch (DataAccessException repositoryProblem)
    {
      throw new AuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem);
    }
    if (loadedUser == null) {
      throw new AuthenticationServiceException(
        "UserDetailsService returned null, which is an interface contract violation");
    }
    return loadedUser;
  }
  
  public void setPasswordEncoder(PasswordEncoder passwordEncoder)
  {
    this.passwordEncoder = passwordEncoder;
  }
  
  protected PasswordEncoder getPasswordEncoder()
  {
    return this.passwordEncoder;
  }
  
  public void setSaltSource(SaltSource saltSource)
  {
    this.saltSource = saltSource;
  }
  
  protected SaltSource getSaltSource()
  {
    return this.saltSource;
  }
  
  public void setUserDetailsService(UserDetailsService userDetailsService)
  {
    this.userDetailsService = userDetailsService;
  }
  
  protected UserDetailsService getUserDetailsService()
  {
    return this.userDetailsService;
  }
  
  protected boolean isIncludeDetailsObject()
  {
    return this.includeDetailsObject;
  }
  
  /**
   * @deprecated
   */
  public void setIncludeDetailsObject(boolean includeDetailsObject)
  {
    this.includeDetailsObject = includeDetailsObject;
  }
}
